package com.a2v2.online.store.database.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.sql.Timestamp;
import java.util.List;

import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.a2v2.online.store.database.dao.contract.GenericDAO;
import com.a2v2.online.store.database.entity.BaseEntity;

/**
 *
 * @author aurionPro
 *
 * @param <T> Type of persistent class
 * @param <ID> Type of Id filed (generally will be Long)
 * Generic DAO contract for hibernate.
 */
@Repository
public class GenericHibernateDAO<T extends BaseEntity, ID extends Serializable>
implements GenericDAO<T, ID>  {
	/** apache logger to log useful information.  */
	static final Logger LOGGER = Logger.getLogger(GenericHibernateDAO.class);

	/** Spring injected hibernate session factory to connect DB. */
	@Autowired
	private SessionFactory sessionFactory;

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	/** Represent the type of entity class. */
	private Class<T> persistentClass;

	/**
	 * Default constructor responsible to get persistence class type.
	 */
	@SuppressWarnings("unchecked")
	public GenericHibernateDAO() {
		this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).
		getActualTypeArguments()[0];
	}

	/**
	 * {@inheritDoc}
	 */

	public final T getById(final ID pId, final boolean pLock) {
		@SuppressWarnings("unchecked")
		T anEntity  = (T) this.sessionFactory.getCurrentSession().get(getPersistentClass(), pId);
		return anEntity;
	}

	/**
	 * {@inheritDoc}
	 */
	public final T getById(final ID pId) {

		return getById(pId, false);
	}
	/**
	 * {@inheritDoc}
	 */
	public final T add(final T pObj) {
		pObj.setCreatedOn(new Timestamp(System.currentTimeMillis()));
		pObj.setModifiedOn(new Timestamp(System.currentTimeMillis()));
		getCurrrentSession().save(pObj);
		return pObj;
	}

	/**
	 * {@inheritDoc}
	 */
	public void attach(final T pObj) {
		//TODO getCurrrentSession().
	}

	/**
	 * {@inheritDoc}
	 */
	public final void clearMemoryCache() {
		getCurrrentSession().clear();
	}

	/**
	 * {@inheritDoc}
	 */
	public final void delete(final T pObj) {
		getCurrrentSession().delete(pObj);
	}

	/**
	 * {@inheritDoc}
	 */
	public final void update(final T pObj) {
		pObj.setModifiedOn(new Timestamp(System.currentTimeMillis()));
		getCurrrentSession().update(pObj);
	}

	/**
	 * {@inheritDoc}
	 */
	public final T saveOrUpdate(final T pObj) {
		if (pObj.getId() == null) {
			pObj.setCreatedOn(new Timestamp(System.currentTimeMillis()));
		}
		pObj.setModifiedOn(new Timestamp(System.currentTimeMillis()));
		getCurrrentSession().saveOrUpdate(pObj);
		return pObj;
	}

	/**
	 * {@inheritDoc}
	 */
	public final List<T> getAll() {

		Criteria aCriteria = getCurrrentSession().createCriteria(
				this.getPersistentClass());
		return aCriteria.list();
	}

	/**
	 * {@inheritDoc}
	 */
	public final List<T> getTop(final int pCount) {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	public void detach(final T pObj) {
		//TODO getCurrrentSession()
	}

	/**
	 * {@inheritDoc}
	 */
	public final void flush() {
		getCurrrentSession().flush();
	}

	/**
	 * To get the persistent class of entity.
	 *
	 * @return persistenet class
	 */
	public final Class<T> getPersistentClass() {
		return this.persistentClass;
	}

	/**
	 * Get the current hibernate session for required db
	 * operation, extending classes use it do more specific
	 * db operations.
	 * @return current session
	 */
	public final Session getCurrrentSession() {
		if (this.sessionFactory == null) {
			LOGGER.fatal("CSCP DAO Exception sessionFactory is null");
		}
		return this.sessionFactory.getCurrentSession();
	}
	@SuppressWarnings("unchecked")
	@Override
	public <FT> T getByFieldName(String fieldName, FT fieldValue) {
		Criteria aCriteria = getCurrrentSession().createCriteria(this.getPersistentClass());
	/*	if (fieldName.split(".").length > 0){
			aCriteria.createAlias(fieldName, "");
		}*/
		return (T)aCriteria.add(Restrictions.eq(fieldName, fieldValue)).uniqueResult();
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public <FT> java.util.List<T> getAllByFieldName(String fieldName, FT fieldValue) {
		Criteria aCriteria = getCurrrentSession().createCriteria(this.getPersistentClass());
		return aCriteria.add(Restrictions.eq(fieldName, fieldValue)).list();
	}

}